// -*-mode:c++; coding:utf-8-*-

#include <iostream>
#include <string>

using namespace std;

#include <boost/asio.hpp>
#include <boost/cast.hpp>

#include "io_service_pool_impl.hpp"
#include "session.hpp"
#include "session_manager.hpp"
#include "protocol.hpp"
#include "async_write.hpp"

using namespace xbase;

typedef session::socket_type socket_type;
typedef socket_type::endpoint_type endpoint_type;

typedef boost::system::error_code error_code;

io_service_pool_ptr pool_ptr;
session_manager_ptr sm_ptr;

void s_async_query_root_meta(std::size_t left_times,
			     session_manager &sm,
			     endpoint_type ep,
			     session_ptr session);

void s_on_connected(std::size_t left_times,
		    session_manager &sm,
		    endpoint_type ep,
		    session_ptr ptr,
		    const error_code &ec);

void on_got_root_meta(protocol::packet_ptr ptr,
		      const error_code &ec)
{
	typedef protocol::packet_type<protocol::ROOT_META_RESP>::type packet_type;
	typedef protocol::packet_type<protocol::ROOT_META_RESP>::ptr_type packet_ptr;
	
	if(ec)
	{
		std::cout << "query root meta failed : " << ec
			  << " - " << ec.message() << std::endl;
	}
	else
	{
		std::cout << "recv resp : " << ptr->to_string() << std::endl;
		
		packet_type *p = boost::polymorphic_downcast<packet_type*>(ptr.get());
		
		const protocol::root_meta_response &resp = p->body();
		std::cout << "root meta address is "
			  << resp.m_server_address.to_string()
			  << std::endl;

		std::cout << "root meta tablet info is "
			  << resp.m_tablet.to_string()
			  << std::endl;
	}
}

void async_query_root_meta(session_ptr session)
{
	typedef protocol::packet_type<protocol::ROOT_META_REQ>::type packet_type;
	typedef protocol::packet_type<protocol::ROOT_META_REQ>::ptr_type packet_ptr;
	
	packet_ptr ptr(new packet_type());
	std::cout << "send query : " << ptr->to_string() << std::endl;
	session->async_write(ptr,
			     boost::bind(&on_got_root_meta,
					 _1, _2));
}


void s_on_got_root_meta(std::size_t left_times,
			const std::string &type,
			session_manager &sm,
			endpoint_type ep,
			protocol::packet_ptr ptr,
			const error_code &ec)
{
	on_got_root_meta(ptr, ec);
	if(left_times > 0)
	{
		if(type == "2-step")
		{
			sm.async_connect(ep,
					 boost::bind(s_on_connected,
						     (left_times - 1),
						     boost::ref(sm), ep,
						     _1, _2));
		}
		else
		{
			xbase::async_write(sm, ep, ptr,
					   boost::bind(s_on_got_root_meta,
						       (left_times - 1),
						       type,
						       boost::ref(sm), ep,
						       _1, _2));
		}
	}
}


void on_connected(session_ptr ptr,
		  const error_code &ec)
{
	if(ec)
	{
		std::cout << "connect error: " << ec
			  << " - " << ec.message() << std::endl;
	}
	else
	{
		async_query_root_meta(ptr);
	}
}

void s_on_connected(std::size_t left_times,
		    session_manager &sm,
		    endpoint_type ep,
		    session_ptr ptr,
		    const error_code &ec)
{
	if(ec)
	{
		std::cout << "connect error: " << ec
			  << " - " << ec.message() << std::endl;
	}
	else
	{
		s_async_query_root_meta(left_times, sm, ep, ptr);
	}
}

void s_async_query_root_meta(std::size_t left_times,
			     session_manager &sm,
			     endpoint_type ep,
			     session_ptr session)
{
	typedef protocol::packet_type<protocol::ROOT_META_REQ>::type packet_type;
	typedef protocol::packet_type<protocol::ROOT_META_REQ>::ptr_type packet_ptr;
	
	packet_ptr ptr(new packet_type());
	std::cout << "send query : " << ptr->to_string() << std::endl;
	session->async_write(ptr,
			     boost::bind(&s_on_got_root_meta,
					 left_times, "2-step",
					 boost::ref(sm), ep,
					 _1, _2));
}


int main(int argc, char **argv)
{
	LOG_CONFIG("../../conf/log4cplus.tablet_server.conf");

	if(argc < 4 || argc > 7)
	{
		cout << "test_session <ip> <port> <times> [type] [mode] [q]" << endl;
		cout << "\t type: 2-step 采用二步方式，即先获取连接，再发送消息" << endl;
		cout << "\t       others 采用直接方式，即通过连接管理直接发送消息" << endl;
		cout << "\t mode: s      采用串行方式，即一次请求收发完成后，才发起第二次请求" << endl;
		cout << "\t       others 采用并行方式，即前一次请求提交后，马上发起第二次请求" << endl;
		cout << "\t q 运行完后马上退出" << endl;
		
		return 0;
	}

	boost::asio::ip::tcp::endpoint endpoint(
		boost::asio::ip::address::from_string(argv[1]),
		::atoi(argv[2])
		);

	std::size_t times = ::atoi(argv[3]);
	std::string type = (argc >= 5 ? argv[4] : "");
	std::string mode = (argc >= 6 ? argv[5] : "");
	std::string q = (argc >= 7 ? argv[6] : "");

	//
	// 首先初始化service_pool并启动
	// 

	boost::shared_ptr<io_service_pool> pool(new impl::single_io_service());
	client_options options;
	pool->set_options(options);
	pool_ptr = pool;
	pool->run();

	// 执行异步操作

	sm_ptr.reset(new_async_module(session_manager, *pool_ptr));

TAG_BEGIN:

	if(type == "2-step")
	{
		// 串行执行，即查询完一次后才发起第二次
		if(mode == "s")
		{
			sm_ptr->async_connect(endpoint,
					 boost::bind(s_on_connected,
						     (times - 1),
						     boost::ref(*sm_ptr), endpoint,
						     _1, _2));
		}
		else
			for(std::size_t i = 0; i < times; ++i)
			{
				sm_ptr->async_connect(endpoint,
						 boost::bind(on_connected, _1, _2));
			}
	}
	else
	{
		typedef protocol::packet_type<protocol::ROOT_META_REQ>::type packet_type;
		typedef protocol::packet_type<protocol::ROOT_META_REQ>::ptr_type packet_ptr;
		
		packet_ptr ptr(new packet_type());
		std::cout << "send query : " << ptr->to_string()
			  << ", times = " << times << std::endl;

		if(mode == "s")
		{
			xbase::async_write(*sm_ptr,
					   endpoint,
					   ptr,
					   boost::bind(s_on_got_root_meta,
						       (times - 1), type,
						       boost::ref(*sm_ptr), endpoint,
						       _1, _2));
		}
		else
			for(std::size_t i = 0; i < times; ++i)
			{
				xbase::async_write(*sm_ptr,
						   endpoint,
						   ptr,
						   boost::bind(on_got_root_meta, _1, _2));
			}
	}

	std::string input;
	while((q != "q") && (std::cin >> input))
	{
		if(input == "close")
		{
			sm_ptr->close();
		}
		else if(input == "quit")
		{
			break;
		}
		else if(input == "restart")
		{
			std::cin >> times;
			std::cin >> type;
			std::cin >> mode;
			std::cin >> q;
			goto TAG_BEGIN;
		}
	}

	pool->stop();
	pool->join();

	
	return 0;
}
